package meta

import (
	"log"
	"moss/conf"
	"time"
)

type Bucket struct {
	Name             string `gorm:"primary_key"`
	CreationDate     string `gorm:"not null"`
	Location         string `gorm:"not null"`
	OwnerAccessKeyId string `gorm:"not null"`
	Permission       string `gorm:"not null"`
}

type RefererList struct {
	BucketName        string `gorm:"primary_key"`
	Referer           string
	LastModified      string `gorm:"not null"`
	OwnerAccessKeyId  string `gorm:"not null"`
	AllowEmptyReferer bool   `grom:"not null"`
}

type Logging struct {
	BucketName       string `gorm:"primary_key"`
	TargetBucket     string `gorm:"not null"`
	TargetPrefix     string
	OwnerAccessKeyId string `gorm:"not null"`
}

type LifeCycle struct {
	ID                     string `gorm:"primary_key"`
	BucketName             string `gorm:"not null"`
	Prefix                 string `gorm:"not null"`
	OwnerAccessKeyId       string `gorm:"not null"`
	CreateTime             string `gorm:"not null"`
	Status                 bool   `gorm:"not null"`
	Days                   uint
	CreatedBeforeDate      string
	AbortDays              uint
	AbortCreatedBeforeData string
}

type Website struct {
	BucketName       string `gorm:"primary_key"`
	OwnerAccessKeyId string `gorm:"not null"`
	Suffix           string `gorm:"not null"`
	Key              string
}

func InitBucket() {
	if !dbHook.HasTable(&Bucket{}) {
		dbHook.CreateTable(&Bucket{})
	}
}

func InitRefererList() {
	if !dbHook.HasTable(&RefererList{}) {
		dbHook.CreateTable(&RefererList{})
	}
}

func InitLogging() {
	if !dbHook.HasTable(&Logging{}) {
		dbHook.CreateTable(&Logging{})
	}
}

func InitWebsite() {
	if !dbHook.HasTable(&Website{}) {
		dbHook.CreateTable(&Website{})
	}
}

func InitLifeCycle() {
	if !dbHook.HasTable(&LifeCycle{}) {
		dbHook.CreateTable(&LifeCycle{})
	}
}

func CreateBucket(bucketName string, location string, accessKeyId string, permission string) bool {
	_, ok := GetBucketInfo(bucketName)
	if ok {
		return false
	} else {
		resp := dbHook.Create(&Bucket{
			CreationDate:     time.Now().Format(conf.OssTimeFormat),
			Location:         location,
			Name:             bucketName,
			OwnerAccessKeyId: accessKeyId,
			Permission:       permission})
		if resp.Error != nil {
			log.Println(resp.Error.Error())
			return false
		}
	}
	return true
}

func SetBucketPermission(bucketMeta *Bucket, newPermission string) bool {
	resp := dbHook.Model(bucketMeta).Update("permission", newPermission)
	if resp.Error != nil {
		log.Println(resp.Error.Error())
		return false
	}
	return true
}

func GetBucketInfo(bucketName string) (*Bucket, bool) {
	var bucket Bucket
	resp := dbHook.Find(&bucket, "name = ?", bucketName)
	if resp.RecordNotFound() {
		return nil, false
	} else if resp.Error != nil {
		log.Println(resp.Error.Error())
		return nil, false
	}
	return &bucket, true
}

func CountObjects(bucketName string) int {
	objectList := GetAllObjects(bucketName, "")
	return len(objectList)
}

func GetMyBucketsInfo(accessKeyId string) []Bucket {
	var bucketList []Bucket
	resp := dbHook.Where("owner_access_key_id = ?", accessKeyId).Find(&bucketList)
	if resp.RecordNotFound() {
		return nil
	} else if resp.Error != nil {
		log.Println(resp.Error.Error())
	}
	return bucketList
}

func CountBuckets(accessKeyId string) int {
	bucketList := GetMyBucketsInfo(accessKeyId)
	return len(bucketList)
}

func DeleteBucket(bucketName string, accessKeyId string) bool {
	_, ok := GetBucketInfo(bucketName)
	if !ok {
		return false // cannot duplicate
	}
	resp := dbHook.Delete(&Bucket{
		Name:             bucketName,
		OwnerAccessKeyId: accessKeyId})

	if resp.Error != nil {
		log.Println(resp.Error.Error())
	}
	return true
}

func GetBucketReferer(bucketName string) (*RefererList, bool) {
	var refererList RefererList
	resp := dbHook.Find(&refererList, "bucket_name = ?", bucketName)
	if resp.RecordNotFound() {
		return nil, false
	} else if resp.Error != nil {
		log.Println(resp.Error.Error())
	}
	return &refererList, true
}

func SetRefererList(bucketName string, accessKeyId string, refererList string, allowEmptyReferer bool) bool {
	entry, ok := GetBucketReferer(bucketName)
	if ok {
		entry.BucketName = bucketName
		entry.Referer = refererList
		entry.LastModified = time.Now().Format(time.RFC3339)
		entry.OwnerAccessKeyId = accessKeyId
		entry.AllowEmptyReferer = allowEmptyReferer
		dbHook.Save(entry)
	} else {
		dbHook.Create(&RefererList{
			Referer:           refererList,
			BucketName:        bucketName,
			LastModified:      time.Now().Format(time.RFC3339),
			OwnerAccessKeyId:  accessKeyId,
			AllowEmptyReferer: allowEmptyReferer,
		})
	}

	return true
}

func DeleteRefererList(bucketName string) bool {
	dbHook.Delete(&RefererList{
		BucketName: bucketName,
	})
	return true
}

func GetLifeCycleByBucket(bucketName string) ([]LifeCycle, bool) {
	var lifeCycle []LifeCycle
	resp := dbHook.Find(&lifeCycle, "bucket_name = ?", bucketName)
	if resp.RecordNotFound() {
		return nil, false
	} else if resp.Error != nil {
		log.Println(resp.Error.Error())
	}
	return lifeCycle, len(lifeCycle) != 0
}

func GetLifeCycleById(lifeCycleId string) (*LifeCycle, bool) {
	var lifeCycle LifeCycle
	resp := dbHook.Find(&lifeCycle, "id = ?", lifeCycleId)
	if resp.RecordNotFound() {
		return nil, false
	} else if resp.Error != nil {
		log.Println(resp.Error.Error())
	}
	return &lifeCycle, true
}

func GetAllLifeCycle() ([]LifeCycle, bool) {
	var lifecycle []LifeCycle
	resp := dbHook.Find(&lifecycle)
	if resp.RecordNotFound() {
		return nil, false
	} else if resp.Error != nil {
		log.Println(resp.Error.Error())
	}
	return lifecycle, true
}

func DeleteLifeCycle(bucketName string) bool {
	resp := dbHook.Delete(&LifeCycle{
		BucketName: bucketName,
	})
	if resp.RecordNotFound() {
		return false
	} else if resp.Error != nil {
		log.Println(resp.Error.Error())
	}
	return true
}

func SetLifeCycleRule(bucketName string, accessKeyId string, id string, prefix string, status bool, expirationDays uint, expirationCreatedBeforeDate string, abortDays uint, abortCreatedBeforeDate string) bool {
	createTime := time.Now().Format("2017-12-13")
	entry, ok := GetLifeCycleById(id)
	if ok {
		entry.BucketName = bucketName
		entry.Prefix = prefix
		entry.CreateTime = createTime
		if len(expirationCreatedBeforeDate) != 0 {
			entry.CreatedBeforeDate = expirationCreatedBeforeDate
		}
		if len(abortCreatedBeforeDate) != 0 {
			entry.AbortCreatedBeforeData = abortCreatedBeforeDate
		}
		if expirationDays != 0 {
			entry.Days = expirationDays
		}
		if abortDays != 0 {
			entry.AbortDays = abortDays
		}
		entry.OwnerAccessKeyId = accessKeyId
		entry.Status = status
		dbHook.Save(entry)
	} else {
		dbHook.Create(&LifeCycle{
			ID:                     id,
			BucketName:             bucketName,
			Status:                 status,
			OwnerAccessKeyId:       accessKeyId,
			CreateTime:             createTime,
			Prefix:                 prefix,
			Days:                   expirationDays,
			CreatedBeforeDate:      expirationCreatedBeforeDate,
			AbortDays:              abortDays,
			AbortCreatedBeforeData: abortCreatedBeforeDate,
		})
	}
	return true
}

func GetLogging(bucketName string) (*Logging, bool) {
	var bucketLogging Logging
	resp := dbHook.Find(&bucketLogging, "bucket_name = ?", bucketName)
	if resp.RecordNotFound() {
		return nil, false
	} else if resp.Error != nil {
		log.Println(resp.Error.Error())
	}
	return &bucketLogging, true
}

func GetLoggingList() ([]Logging, bool) {
	var bucketLoggingList []Logging
	resp := dbHook.Find(&bucketLoggingList)
	if resp.RecordNotFound() {
		return nil, false
	} else if resp.Error != nil {
		log.Println(resp.Error.Error())
	}
	return bucketLoggingList, len(bucketLoggingList) != 0
}

func SetLogging(bucketName string, accessKeyId string, targetBucket string, targetPrefix string) bool {
	entry, ok := GetLogging(bucketName)
	if ok {
		entry.BucketName = bucketName
		entry.TargetPrefix = targetPrefix
		entry.TargetBucket = targetBucket
		entry.OwnerAccessKeyId = accessKeyId
		dbHook.Save(entry)
	} else {
		dbHook.Create(&Logging{
			BucketName:       bucketName,
			OwnerAccessKeyId: accessKeyId,
			TargetPrefix:     targetPrefix,
			TargetBucket:     targetBucket,
		})
	}

	return true
}

func DeleteLogging(bucketName string) bool {
	dbHook.Delete(&Logging{
		BucketName: bucketName,
	})
	return true
}

func GetWebsite(bucketName string) (*Website, bool) {
	var bucketWebsite Website
	resp := dbHook.Find(&bucketWebsite, "bucket_name = ?", bucketName)

	if resp.RecordNotFound() {
		return nil, false
	} else if resp.Error != nil {
		log.Println(resp.Error.Error())
	}
	return &bucketWebsite, true
}

func PutWebsite(bucketName string, accessKeyId string, suffix string, key string) bool {
	entry, ok := GetWebsite(bucketName)
	if ok {
		entry.BucketName = bucketName
		entry.Suffix = suffix
		entry.Key = key
		entry.OwnerAccessKeyId = accessKeyId
		dbHook.Save(entry)
	} else {
		dbHook.Create(&Website{
			BucketName:       bucketName,
			OwnerAccessKeyId: accessKeyId,
			Suffix:           suffix,
			Key:              key,
		})
	}
	return true
}

func DeleteWebsite(bucketName string) bool {
	dbHook.Delete(&Website{
		BucketName: bucketName,
	})
	return true
}
